﻿using System;
using System.Collections.Generic;
using sscore;

namespace DM4Script
{
    /// <summary>
    /// clases prototypes
    /// </summary>
    public static class Prototypes
    {
        /// <summary>
        /// log structure
        /// </summary>
        public class Log
        {
            /// <summary>
            /// point
            /// </summary>
            public class Point
            {
                /// <summary>
                /// point x
                /// </summary>
                public Int64 X;

                /// <summary>
                /// point y
                /// </summary>
                public Int64 Y;

                /// <summary>
                /// 
                /// </summary>
                /// <param name="x"></param>
                /// <param name="y"></param>
                public Point(int x, int y)
                {
                    X = x;
                    Y = y;
                }

                /// <summary>
                /// 
                /// </summary>
                /// <param name="x"></param>
                /// <param name="y"></param>
                public Point(Int64 x, Int64 y)
                {
                    X = x;
                    Y = y;
                }

                /// <summary>
                /// 
                /// </summary>
                public Point()
                {                    
                }
            }

            /// <summary>
            /// flag has error
            /// </summary>
            public readonly bool Error;

            /// <summary>
            /// point error for message
            /// </summary>
            public readonly Point PointError;

            /// <summary>
            /// point error for selected
            /// </summary>
            public readonly Point SelectError;

            private readonly string _message;

            /// <summary>
            /// add log message
            /// </summary>
            /// <Params name="message"></Params>
            public Log(string message)
            {
                SelectError = null;
                PointError = null;
                _message = message;
            }

            /// <summary>
            /// add log message and error
            /// </summary>
            /// <Params name="error"></Params>
            /// <Params name="message"></Params>
            public Log(bool error, string message)
                : this(message)
            {
                Error = error;
            }

            /// <summary>
            /// add full log message and error
            /// </summary>
            /// <Params name="error"></Params>
            /// <Params name="message"></Params>
            /// <Params name="errorLine"></Params>
            /// <Params name="errorSimbol"></Params>
            /// <Params name="errorSelStart"></Params>
            /// <Params name="errorSelEnd"></Params>
            public Log(bool error, string message, int errorLine, int errorSimbol, int errorSelStart, int errorSelEnd)
            {
                SelectError = null;
                PointError = null;
                Error = error;
                _message = message;
                PointError = new Point {X = errorLine, Y = errorSimbol};
                SelectError = new Point {X = errorSelStart, Y = errorSelEnd};
            }

            /// <summary>
            /// return log for message
            /// </summary>
            /// <returns></returns>
            public string Message()
            {
                if (Error)
                    if (PointError != null)
                        return String.Format("ERROR: [line:{0},col:{1}] {2})", PointError.X, PointError.Y, _message);
                    else
                        return String.Format("ERROR: {0}", _message);
                return _message;
            }
        }


        /// <summary>
        /// ScriptTask - Этот класс представляет "прототип" для TASK
        /// </summary>
        public class ScriptTask : ExprAst
        {
            /// <summary>
            /// state task
            /// </summary>
            public StateTask State;

            /// <summary>
            /// task name
            /// </summary>
            public readonly string TaskName;

            /// <summary>
            /// condition for the main task
            /// </summary>
            public readonly bool Main;

            /// <summary>
            /// name modile
            /// </summary>
            public readonly string ModuleName;

            /// <summary>
            /// id task id table [tasks]
            /// </summary>
            public long TskId = ScriptConst.DefaultNULLId;

            /// <summary>
            /// id script field [rsh_id]
            /// </summary>
            public int ScrId = ScriptConst.DefaultNULLId;

            /// <summary>
            /// id session field [rsh_session_id]
            /// </summary>
            public string RshSessionId = "";

            /// <summary>
            /// id task field [tsk_session_id]
            /// </summary>
            public string TskSessionId
            {
                get { return String.Format("{0}_{1}", RshSessionId, TaskName); }
            }

            /// <summary>
            /// id table [users]
            /// </summary>
            public int UsrId = ScriptConst.DefaultNULLId;

            /// <summary>
            /// main parameters for task
            /// </summary>
            public readonly Dictionary<string, ParamEntry> Params;

            /// <summary>
            /// secondary parameters for module
            /// </summary>
            public readonly Dictionary<string, ParamEntry> SubParams;

            /// <summary>
            /// results of the task
            /// </summary>
            public Dictionary<string, ParamEntry> Result;

            /// <summary>
            /// total work time for task
            /// </summary>
            public int TotalWorkTime = 0;

            /// <summary>
            /// total trafic for task
            /// </summary>
            public long TotalTraffic = 0;

            /// <summary>
            /// creates task object
            /// </summary>
            /// <Params name="taskName"></Params>
            /// <Params name="moduleName"></Params>
            /// <Params name="params"></Params>
            /// <Params name="SubParams"></Params>
            /// <Params name="main"></Params>
            public
                ScriptTask(string taskName, string moduleName, Dictionary<string, ParamEntry> @params,
                           Dictionary<string, ParamEntry> subParams, bool main = true)
            {
                TaskName = taskName;
                ModuleName = moduleName;
                Params = @params;
                SubParams = subParams;
                Main = main;
                State = StateTask.NotStarted;
            }
        };

        /// <summary>
        /// prototype for DECLARE
        /// </summary>
        public class DeclareSection
        {
            /// <summary>
            /// creates DECLARE object
            /// </summary>
            /// <Params name="tasks"></Params>
            public
                DeclareSection(Dictionary<string, ScriptTask> tasks)
            {
                Tasks = tasks;
            }

            /// <summary>
            /// return tasks
            /// </summary>
            public Dictionary<string, ScriptTask> Tasks;
        };

        /// <summary>
        /// ScriptTask - Этот класс представляет "прототип" для TASK
        /// </summary>
        public class TaskRun
        {
            /// <summary>
            /// state for OnTASK
            /// </summary>
            public StateOnTask State;

            /// <summary>
            /// dictonary ScriptTask
            /// </summary>
            public readonly Dictionary<string, ScriptTask> TasksOn;

            /// <summary>
            /// command for runs
            /// </summary>
            public ExprAst RunComand;

            /// <summary>
            /// creates OnTask
            /// </summary>
            /// <Params name="tasksOn"></Params>
            /// <Params name="runComand"></Params>
            public
                TaskRun(Dictionary<string, ScriptTask> tasksOn, ExprAst runComand)
            {
                TasksOn = tasksOn;
                RunComand = runComand;
                State = StateOnTask.NotStarted;
            }

            /// <summary>
            /// setting the result
            /// </summary>
            public bool SetResult()
            {
                return SetResult(ref RunComand);
            }

            private bool SetResult(ref ExprAst exprAst)
            {
                try
                {
                    if (exprAst is IfExprAst)
                        SetResult(ref ((IfExprAst) exprAst).Cond);
                    else if (exprAst is BinaryExprAst)
                    {
                        SetResult(ref ((BinaryExprAst) exprAst).Lhs);
                        SetResult(ref ((BinaryExprAst) exprAst).Rhs);
                    }
                    else if (exprAst is Property)
                    {
                        ScriptTask task = TasksOn[((Property) exprAst).TaskName];
                        ParamEntry paramEntry = task.Result[((Property) exprAst).Prop];
                        exprAst = new NumberExprAst((string) paramEntry.GetValue());
                    }
                }
                catch (Exception)
                {
                    return false;
                }
                return true;
            }

            /// <summary>
            /// execution the condition
            /// </summary>
            /// <returns></returns>
            public Queue<ExprAst> Execute()
            {
                ExprAst run = execute(RunComand);

                Queue<ExprAst> commandAndTask = new Queue<ExprAst>();
                if (run is CommAndStask)
                    commandAndTask = ((CommAndStask) run).CommandAndTask;
                else if (run != null) commandAndTask.Enqueue(run);

                return commandAndTask;
            }

            private static ExprAst execute(ExprAst exprAst)
            {
                IfExprAst ifExprAst = exprAst as IfExprAst;
                if (ifExprAst != null)
                    return (ifExprAst).Execute();
                return exprAst;
            }
        };

        /// <summary>
        /// PrototypeRUN - Этот класс представляет "прототип" для RUNSection
        /// </summary>
        public class RunSection
        {
            /// <summary>
            /// 
            /// </summary>
            public Queue<TaskRun> TasksRun;

            /// <summary>
            /// 
            /// </summary>
            /// <Params name="tasksRun"></Params>
            public
                RunSection(Queue<TaskRun> tasksRun)
            {
                TasksRun = tasksRun;
            }
        };

        /// <summary>
        /// ScriptTask - Этот класс представляет "прототип" для Property
        /// </summary>
        public class Property : ExprAst
        {
            /// <summary>
            /// task name 
            /// </summary>
            public string TaskName;

            /// <summary>
            /// property result
            /// </summary>
            public string Prop;

            /// <summary>
            /// constructor
            /// </summary>
            /// <Params name="taskName"></Params>
            /// <Params name="prop"></Params>
            public
                Property(string taskName, string prop)
            {
                TaskName = taskName;
                Prop = prop;
            }
        };


        /// <summary>
        /// ScriptTask - Этот класс представляет "прототип" для Command
        /// </summary>
        public class Command : ExprAst
        {
            /// <summary>
            /// command name
            /// </summary>
            public string CommandName;

            /// <summary>
            /// constructor
            /// </summary>
            /// <Params name="commandName"></Params>
            public
                Command(string commandName)
            {
                CommandName = commandName;
            }
        };

        /// <summary>
        /// NumberExprAST - Класс узла выражения для числовых литералов (Например, "1.0").
        /// </summary>
        public class NumberExprAst : ExprAstOper
        {
            private readonly double _val;

            /// <summary>
            /// constructor
            /// </summary>
            /// <Params name="val"></Params>
            public NumberExprAst(double val)
            {
                _val = val;
            }

            /// <summary>
            /// constructor
            /// </summary>
            /// <Params name="val"></Params>
            public NumberExprAst(string val)
            {
                _val = Convert.ToDouble(val);
            }

            /// <summary>
            /// for the logical function
            /// </summary>
            /// <returns></returns>
            public override bool RetBool()
            {
                return _val > 0;
            }

            /// <summary>
            /// for the mathematical function
            /// </summary>
            /// <returns></returns>
            public override double RetDouble()
            {
                return _val;
            }
        };

        /// <summary>
        /// ExprAST - Базовый класс для всех узлов выражений.
        /// </summary>
        public abstract class ExprAst
        {
        };

        /// ExprAST - Базовый класс для булевых выражений.
        public abstract class ExprAstOper : ExprAst
        {
            /// <summary>
            /// for the logical function
            /// </summary>
            /// <returns></returns>
            public abstract bool RetBool();

            /// <summary>
            /// for the mathematical function
            /// </summary>
            /// <returns></returns>
            public abstract double RetDouble();
        };

        /// <summary>
        /// BinaryExprAST - Класс узла выражения для бинарных операторов.
        /// </summary>
        public class BinaryExprAst : ExprAstOper
        {
            /// <summary>
            /// the left operation
            /// </summary>
            public ExprAst Lhs;

            /// <summary>
            /// operation
            /// </summary>
            public string Operation;

            /// <summary>
            /// the rigth operation
            /// </summary>
            public ExprAst Rhs;

            /// <summary>
            /// constructor
            /// </summary>
            /// <Params name="op"></Params>
            /// <Params name="lhs"></Params>
            /// <Params name="rhs"></Params>
            public BinaryExprAst(string op, ExprAst lhs, ExprAst rhs)
            {
                Operation = op;
                Lhs = lhs;
                Rhs = rhs;
            }

            /// <summary>
            /// for the mathematical function
            /// </summary>
            /// <returns></returns>
            public override double RetDouble()
            {
                switch (Operation)
                {
                    case "+":
                        return ((ExprAstOper) Lhs).RetDouble() + ((ExprAstOper) Rhs).RetDouble();
                    case "-":
                        return ((ExprAstOper) Lhs).RetDouble() - ((ExprAstOper) Rhs).RetDouble();
                    case "/":
                        return ((ExprAstOper) Lhs).RetDouble()/((ExprAstOper) Rhs).RetDouble();
                    case "*":
                        return ((ExprAstOper) Lhs).RetDouble()*((ExprAstOper) Rhs).RetDouble();
                }

                return 0;
            }

            /// <summary>
            /// for the logical function
            /// </summary>
            /// <returns></returns>
            public override bool RetBool()
            {
                switch (Operation)
                {
                    case "OR":
                        return ((ExprAstOper) Lhs).RetBool() || ((ExprAstOper) Rhs).RetBool();
                    case "AND":
                        return ((ExprAstOper) Lhs).RetBool() && ((ExprAstOper) Rhs).RetBool();
                    case "<":
                        return ((ExprAstOper) Lhs).RetDouble() < ((ExprAstOper) Rhs).RetDouble();
                    case ">":
                        return ((ExprAstOper) Lhs).RetDouble() > ((ExprAstOper) Rhs).RetDouble();
                    case "==":
                        return Math.Abs(((ExprAstOper) Lhs).RetDouble() - ((ExprAstOper) Rhs).RetDouble()) < 0.0000001;
                    case "!=":
                        return Math.Abs(((ExprAstOper) Lhs).RetDouble() - ((ExprAstOper) Rhs).RetDouble()) > 0.0000001;
                    case "<=":
                        return ((ExprAstOper) Lhs).RetDouble() <= ((ExprAstOper) Rhs).RetDouble();
                    case ">=":
                        return ((ExprAstOper) Lhs).RetDouble() >= ((ExprAstOper) Rhs).RetDouble();
                }

                return true; // numberExprAST.Val > 0;
            }
        };

        /// <summary>
        /// IfExprAST - Класс узла выражения для if/then/else.
        /// </summary>
        public class IfExprAst : ExprAst
        {
            /// <summary>
            /// the condition
            /// </summary>
            public ExprAst Cond;

            /// <summary>
            /// then operation
            /// </summary>
            public ExprAst ThenIf;

            /// <summary>
            /// else operation
            /// </summary>
            public ExprAst ElseIf;

            /// <summary>
            /// constructor
            /// </summary>
            /// <Params name="cond"></Params>
            /// <Params name="thenIf"></Params>
            /// <Params name="elseIf"></Params>
            public IfExprAst(ExprAst cond, ExprAst thenIf, ExprAst elseIf)
            {
                Cond = cond;
                ThenIf = thenIf;
                ElseIf = elseIf;
            }

            /// <summary>
            /// execute operation if
            /// </summary>
            /// <returns></returns>
            public ExprAst Execute()
            {
                ExprAstOper exprAstOper = Cond as ExprAstOper;
                if (exprAstOper != null)
                {
                    if ((exprAstOper).RetBool())
                        return ThenIf;
                    return ElseIf;
                }
                return null;
            }
        };

        /// <summary>
        /// CommandStask - Класс узла выражения для if/then/else.
        /// </summary>
        public class CommAndStask : ExprAst
        {
            /// <summary>
            /// the queue commands
            /// </summary>
            public Queue<ExprAst> CommandAndTask;

            /// <summary>
            /// constructor
            /// </summary>
            /// <Params name="commandAndTask"></Params>
            public CommAndStask(Queue<ExprAst> commandAndTask)
            {
                CommandAndTask = commandAndTask;
            }
        };


        /// <summary>
        /// state task
        /// </summary>
        public enum StateTask
        {
            /// <summary>
            /// the task not started 
            /// </summary>
            NotStarted,

            /// <summary>
            /// the task is runs
            /// </summary>
            Runs,

            /// <summary>
            /// the task is completed
            /// </summary>
            Completed,

            /// <summary>
            /// the task is wait
            /// </summary>
            Wait
        }

        /// <summary>
        /// state onTask
        /// </summary>
        public enum StateOnTask
        {
            /// <summary>
            /// the onTask not started 
            /// </summary>
            NotStarted,

            /// <summary>
            /// the onTask is completed
            /// </summary>
            Completed
        }
    }
}
